#include "stdafx.h"

#include "parser_internal.h"
#include "parser_state.h"
#include "parser_errors.h"

#include "ast_literals.h"

namespace funpl
{

namespace parser
{

/// Parses a character literal.
ast::char_literal* parse_char_literal(parser_state &state, ast::node *parent)
{
	return state.tree->create_node<ast::char_literal>(
			parent,
			expect_cont(state, token_type::tok_char_literal).token.range
		);
}

/// Parses a string literal.
ast::string_literal* parse_string_literal(parser_state &state, ast::node *parent)
{
	return state.tree->create_node<ast::string_literal>(
			parent,
			expect_cont(state, token_type::tok_string_literal).token.range
		);
}

/// Numeric literal description.
struct num_literal_desc
{
	bool float_;
	bool single;

	bool unsigned_;
	bool hex;
	bool octet;
	bool short_;
	bool long_;

	/// Constructor.
	num_literal_desc()
		: float_(),
		single(),
		unsigned_(),
		hex(),
		octet(),
		short_(),
		long_() { }
};

/// Gets information on the given raw numeric literal.
num_literal_desc num_literal_info(util::const_char_range &raw)
{
	num_literal_desc desc;

	bool endOfSuffixes = false;

	// Suffixes
	for (auto it = raw.end(); !endOfSuffixes && it-- > raw.begin(); )
	{
		switch (*it)
		{
		case 'f':
		case 'F':
			desc.single = true;
			break;

		case 'u':
		case 'U':
			desc.unsigned_ = true;
			break;
		case 'o':
		case 'O':
			desc.octet = true;
			break;
		case 's':
		case 'S':
			desc.short_ = true;
			break;
		case 'l':
		case 'L':
			desc.long_ = true;
			break;

		default:
			raw.end() = it + 1;
			endOfSuffixes = true;
		}
	}

	// Floating-point
	desc.float_ = (memchr(raw.begin(), '.', raw.size()) != nullptr);

	// Hexadecimal
	if (_strnicmp(raw.begin(), "0x", 2) == 0)
	{
		raw.begin() += 2;
		desc.hex = true;
	}

	return desc;
}

/// Gets a numeric literal type from the given description.
ast::num_literal_type::t float_literal_type(parser_state &state, const num_literal_desc &desc)
{
	if (desc.long_ || desc.short_ || desc.octet || desc.unsigned_ || !desc.float_)
		FUNPL_PARSE_ERROR(state, debug::error_code::invalid_num_literal, "unexpected trailing characters in floating-point literal");

	return (desc.single) ? ast::num_literal_type::float_ : ast::num_literal_type::double_;
}

/// Gets a numeric literal type from the given description.
ast::num_literal_type::t int_literal_type(parser_state &state, const num_literal_desc &desc)
{
	if (desc.long_ + desc.short_ + desc.octet > 1 || desc.single || desc.float_)
		FUNPL_PARSE_ERROR(state, debug::error_code::invalid_num_literal, "unexpected trailing characters in integral literal");

	if (desc.long_)
		return ast::num_literal_type::long_;
	else if (desc.short_)
		return ast::num_literal_type::short_;
	else if (desc.octet)
		return ast::num_literal_type::byte;
	else
		return ast::num_literal_type::int_;
}

/// Parses a numeric literal.
template <class Value>
ast::typed_num_literal<Value>* create_typed_num_literal(parser_state &state, ast::node *parent, Value value)
{
	auto literal_ = state.tree->create_node< ast::typed_num_literal<Value> >(parent);
	
	literal_->value = value;

	return literal_;
}

/// Parses a numeric literal.
ast::num_literal* parse_num_literal(parser_state &state, ast::node *parent)
{
	ast::num_literal *literal_ = nullptr;

	util::const_char_range raw = expect_cont(state, token_type::tok_num_literal).token.range;
	num_literal_desc desc = num_literal_info(raw);

	const char *parsedEnd = nullptr;

	// Floating-point literals
	if (desc.float_)
	{
		// Double value
		literal_ = create_typed_num_literal( state, parent, strtod(raw.begin(), const_cast<char**>(&parsedEnd)) );

		// Precision modifiers
		literal_->type = float_literal_type(state, desc);

		// TODO: literal bounds checking
		// TODO: IEEE infinity & NaN literals?

		// Make sure entire literal is valid
		if (parsedEnd != raw.end())
			FUNPL_PARSE_ERROR(state, debug::error_code::invalid_num_literal, "unexpected characters in floating-point literal");
		if (desc.hex)
			FUNPL_PARSE_ERROR(state, debug::error_code::invalid_num_literal, "hexadecimal notation unsupported for floating-point literals");
	}
	else
	{
		int radix = (desc.hex) ? 16 : 10;

		if (desc.long_)
		{
			if (desc.unsigned_)
				// Unsigned long value
				literal_ = create_typed_num_literal<unsigned long long>( state, parent, _strtoui64(raw.begin(), const_cast<char**>(&parsedEnd), radix) );
			else
				// Signed long value
				literal_ = create_typed_num_literal<long long>( state, parent, _strtoi64(raw.begin(), const_cast<char**>(&parsedEnd), radix) );
		}
		else
		{
			if (desc.unsigned_)
				// Unsigned value
				literal_ = create_typed_num_literal<unsigned long>( state, parent, strtoul(raw.begin(), const_cast<char**>(&parsedEnd), radix) );
			else
				// Signed value
				literal_ = create_typed_num_literal<long>( state, parent, strtol(raw.begin(), const_cast<char**>(&parsedEnd), radix) );
		}
		
		// Precision modifiers
		literal_->type = int_literal_type(state, desc);

		// TODO: literal bounds checking

		// Make sure entire literal is valid
		if (parsedEnd != raw.end())
			FUNPL_PARSE_ERROR(state, debug::error_code::invalid_num_literal, "unexpected characters in integral literal");
	}

	return literal_;
}

} // namespace

} // namespace